`include "defines.v"
module alu(
  input [`OP_W-1:0]   mode    ,
  input               rv32mode,
  input [`XLEN-1:0]   op1     , 
  input [`XLEN-1:0]   op2     ,
  output[`XLEN-1:0]   out     
);
wire [5:0]       shamt = op2[5:0]                        ;
wire [31:0]      srl_1 = op1[31:0] >> shamt[4:0]         ;   
wire [31:0]      sra_1 = $signed(op1[31:0]) >>> shamt[4:0];
wire [31:0]      sll_1 = op1[31:0] << shamt[4:0]         ;
wire [`XLEN-1:0] sll_0 = op1 << shamt                    ;
wire [`XLEN-1:0] sra_0 = $signed(op1) >>> shamt          ;
wire [`XLEN-1:0] srl_0 = op1 >> shamt                    ;

wire [`XLEN-1:0] sll_out = rv32mode ? {32'd0,sll_1} : sll_0 ;
wire [`XLEN-1:0] srl_out = rv32mode ? {32'd0,srl_1} : srl_0 ;
wire [`XLEN-1:0] sra_out = rv32mode ? {32'd0,sra_1} : sra_0 ;

wire [`XLEN-1:0] add_out  = op1 + op2 ;
wire [`XLEN-1:0] sub_out  = op1 - op2 ;
wire [`XLEN-1:0] and_out  = op1 & op2 ;
wire [`XLEN-1:0] or_out   = op1 | op2 ;
wire [`XLEN-1:0] xor_out  = op1 ^ op2 ;
wire [`XLEN-1:0] sltu_out = op1 < op2 ;
wire [`XLEN-1:0] slt_out  = $signed(op1) < $signed(op2);

wire [`XLEN-1:0] out_sel = ({`XLEN{mode == `ALU_ADD }} &  add_out ) |
                           ({`XLEN{mode == `ALU_SUB }} &  sub_out ) |
                           ({`XLEN{mode == `ALU_SLL }} &  sll_out ) |
                           ({`XLEN{mode == `ALU_SRL }} &  srl_out ) |
                           ({`XLEN{mode == `ALU_SRA }} &  sra_out ) |
                           ({`XLEN{mode == `ALU_AND }} &  and_out ) |
                           ({`XLEN{mode == `ALU_OR  }} &  or_out  ) |
                           ({`XLEN{mode == `ALU_XOR }} &  xor_out ) |
                           ({`XLEN{mode == `ALU_SLT }} &  slt_out ) |
                           ({`XLEN{mode == `ALU_SLTU}} &  sltu_out) ;

assign out = rv32mode ? {{32{out_sel[31]}},out_sel[31:0]} : out_sel;
endmodule